package com.ruicar.afs.cloud.batch.dayend.handle.run;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ruicar.afs.cloud.common.core.enums.AfsEnumUtil;
import com.ruicar.afs.cloud.common.core.uid.UidGenerator;
import com.ruicar.afs.cloud.common.modules.contract.enums.AtomComponentType;
import com.ruicar.afs.cloud.common.modules.contract.enums.LendingDirection;
import com.ruicar.afs.cloud.common.modules.contract.enums.VoucherSendStatus;
import com.ruicar.afs.cloud.common.modules.dto.voucher.VoucherBusinessDataDto;
import com.ruicar.afs.cloud.common.modules.dto.voucher.VoucherKeyToQLExpressDto;
import com.ruicar.afs.cloud.common.vo.*;
import com.ruicar.afs.cloud.common.voucher.Voucher;
import com.ruicar.afs.cloud.common.voucher.VoucherAtomMapping;
import com.ruicar.afs.cloud.common.voucher.VoucherBaseInfo;
import com.ruicar.afs.cloud.common.voucher.VoucherRunResult;
import com.ruicar.afs.cloud.common.voucher.handle.VoucherCaches;
import com.ruicar.afs.cloud.common.voucher.handle.VoucherHelper;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.text.MessageFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author infinity
 * @version 1.0
 * @date 2020/8/21 23:00
 */
@Component
@Slf4j
@AllArgsConstructor
public class HandleDataGeneration {
    private final UidGenerator uidGenerator;
    /**
     * 处理获取凭证业务数据参数
     *
     * @param ids
     * @return
     */
    public   VoucherBean getVoucherParam(VoucherFlowInfoVo voucherFlowInfo, List<Long> ids) {
        VoucherBean bean = new VoucherBean();
        bean.setContractNo(voucherFlowInfo.getContractNo());
        bean.setTransNo(voucherFlowInfo.getTransNo());
        bean.setDealerName(voucherFlowInfo.getDealerName());
        bean.setLongs(ids);
        if(voucherFlowInfo.getBusinessType()!=null){
            bean.setCaseType( AfsEnumUtil.desc(voucherFlowInfo.getBusinessType()));
        }
        bean.setKeepAccountDate(voucherFlowInfo.getKeepAccountDate());
        VoucherBusinessDataDto dto = new VoucherBusinessDataDto();
        Map<String, String> mapQl = new HashMap<>();
        //根据凭证事件获取凭证模板信息
        for (Long id : ids) {
            //根据凭证模板id获取凭证模板信息
            VoucherBaseInfo info = VoucherCaches.getVoucherInfo(id);
            //组装获取凭证业务数据DTO
            dto.setVoucherId(info.getId());
            for (VoucherEntryVo entry : info.getEntryList()) {
                getMapQlExpress(VoucherCaches.getVoucher(entry.getId().toString()), mapQl,bean);
            }
        }
        dto.setVoucherKeyToQLExpressDtos(bean.getVoucherKeyToQLExpressDtos());
        bean.setVoucherBusinessDataDto(dto);
        return bean;
    }

    //  获取金额表达式
    protected  Map getMapQlExpress(Voucher voucher, Map<String, String> map,VoucherBean bean ) {
        for (VoucherAtomMapping voucherAtomMapping : voucher.getVoucherAtomMappings()) {
            if (!map.containsKey(voucherAtomMapping.getAtomKey())) {
                VoucherKeyToQLExpressDto dto = new VoucherKeyToQLExpressDto();
                dto.setKey(voucherAtomMapping.getAtomKey());
                //判断原子组件类型，数字类型的直接取值
                if(AtomComponentType.NUMBER.equals(voucherAtomMapping.getAtomType())){
                    map.put(voucherAtomMapping.getAtomKey(), voucherAtomMapping.getValuePath());
                    dto.setAmt(new BigDecimal(voucherAtomMapping.getValuePath().trim()));
                    bean.getDtos().add(dto);
                }else{
                    map.put(voucherAtomMapping.getAtomKey(), getQlExpress(voucherAtomMapping.getValuePath(),bean));
                    dto.setQlExpress(map.get(voucherAtomMapping.getAtomKey()));
                    bean.getVoucherKeyToQLExpressDtos().add(dto);
                }
            }
        }
        return map;
    }

    //  组装凭证表达式
    protected  String getQlExpress(String qlExpress,VoucherBean bean) {
        //替换合同号
        qlExpress=  qlExpress.replaceAll("#contractNo", bean.getContractNo());
        //替换流水号
        return qlExpress.replaceAll("transNo", bean.getTransNo());
    }
    public  List<VoucherAccountVo> saveVoucherInfo(VoucherBusinessDataDto dto,VoucherBean bean) {
        List<AccountVoucherInfoVo>  voucherAccountVos=new ArrayList<>();
        //凭证金额计算参数初始化
        prepareVoucherParam(dto,bean);
        //根据凭证事件获取凭证模板信息
        for (Long id : bean.getLongs()) {
            //根据凭证模板id获取凭证模板信息
            VoucherBaseInfo info = VoucherCaches.getVoucherInfo(id);
            genVoucher(dto, info,bean);
        }
         return bean.getVoucherAccountVos();
    }

    //凭证底表信息保存
    protected  void genVoucher(VoucherBusinessDataDto dataDto, VoucherBaseInfo info,VoucherBean bean) {
        // 1.初始化凭证
        List<AccountVoucherEntryVo> entries = initVoucher(info,bean);
        //判断凭证分录信息数量为0时，不执行后续操作
        if (entries.size() == 0) {
            log.info("凭证流水号：{}，事件代码为：{}，凭证分录信息为零，不生成凭证底表信息！", bean.getTransNo(), info.getVoucherInfo().getChildBusinessCode());
            return;
        }
        // 2.校验凭证
        BigDecimal totalAmount=  vaildVoucher(entries, dataDto.getVoucherId());
        bean.setTotalAmount(totalAmount);
        // 3.保存凭证
        saveVoucher(info.getVoucherInfo(), entries,bean);
    }

    //凭证金额计算参数初始化
    protected  void prepareVoucherParam(VoucherBusinessDataDto dto,VoucherBean bean) {
        Map<String, Object> paramMap = new HashMap<>();
        dto.getVoucherKeyToQLExpressDtos().addAll(bean.getDtos());
        dto.getVoucherKeyToQLExpressDtos().stream().forEach(voucherKeyToQLExpressDto -> {
            Object param = voucherKeyToQLExpressDto.getAmt();
            StringBuilder keyBuilder = new StringBuilder();
            keyBuilder.append(voucherKeyToQLExpressDto.getKey());
            testAndSetParam(keyBuilder.toString(), paramMap, param);
        });
        JSONObject json = JSON.parseObject(JSON.toJSONString(dto));
        log.debug("参数\n{}\n预处理后的参数为\n[{}]", json.toJSONString(), paramMap);
        bean.setMapParam(paramMap);
    }

    /**
     * Test and set param *
     *
     * @param keyPath  key path
     * @param paramMap param map
     * @param object   object
     */
    private  void testAndSetParam(String keyPath, Map<String, Object> paramMap, Object object) {
        if (object instanceof JSONObject) {
            ((JSONObject) object).keySet().forEach(key -> {
                testAndSetParam(keyPath + "." + key, paramMap, ((JSONObject) object).get(key));
            });
        } else if (object instanceof JSONArray) {
            JSONArray array = ((JSONArray) object);
            for (int i = 0; i < array.size(); i++) {
                testAndSetParam(keyPath + "[" + i + "]", paramMap, array.get(i));
            }
        } else {
            paramMap.put(keyPath, object);
        }
    }

    //组装凭证分录
    protected   List<AccountVoucherEntryVo> initVoucher(VoucherBaseInfo info, VoucherBean bean) {
        List<Voucher> list = info.getEntryList().stream().map(entry -> {
            Voucher voucher = VoucherCaches.getVoucher(entry.getId().toString());
            return voucher;
        }).collect(Collectors.toList());
        VoucherRunResult voucherRunResult = VoucherHelper.runVoucher(bean.getMapParam(), list);
        Assert.isTrue(voucherRunResult.getHit(), MessageFormat.format("根据金额表达式获取金额有误！,交易流水号：[{0}]", bean.getTransNo()));
        Map<String, BigDecimal> map = new HashMap<>();
        voucherRunResult.getResults().stream().forEach(result -> {
            String param = result.getRuleNo();
            map.put(param, result.getAmt());
        });
        List<AccountVoucherEntryVo> accountVoucherEntries = new ArrayList<>();
        for (VoucherEntryVo entry : info.getEntryList()) {
            //判断凭证金额为零时保存
            if (map.get(entry.getId().toString()).compareTo(BigDecimal.ZERO) == 0) {
                continue;
            }
            AccountVoucherEntryVo accountVoucherEntry = AccountVoucherEntryVo.convertFrom(entry);
            if (LendingDirection.CREDIT.equals(accountVoucherEntry.getLendingDirection())) {
                accountVoucherEntry.setCreditAmt(map.get(entry.getId().toString()));
                accountVoucherEntry.setDebitAmt(BigDecimal.ZERO);
            } else if (LendingDirection.DEBIT.equals(accountVoucherEntry.getLendingDirection())) {
                accountVoucherEntry.setCreditAmt(BigDecimal.ZERO);
                accountVoucherEntry.setDebitAmt(map.get(entry.getId().toString()));
            }
            accountVoucherEntries.add(accountVoucherEntry);
        }
        return accountVoucherEntries;
    }
    /*
     * 写入凭证信息
     *
     * @return
     */
    private  void saveVoucher(VoucherInfoVo voucherInfo, List<AccountVoucherEntryVo> entries,VoucherBean bean) {
        VoucherAccountVo voucherAccountVo =new VoucherAccountVo();
        AccountVoucherInfoVo vchrInfo = AccountVoucherInfoVo.convertFrom(voucherInfo);
        // 凭证流水号
        String vchrNo = DateUtil.format(new Date(), "dd") + uidGenerator.getUID();
        vchrInfo.setId(uidGenerator.getUID());
        vchrInfo.setVoucherNo(vchrNo);
        vchrInfo.setVoucherSerialNumber(vchrNo);
        vchrInfo.setFiscalYear(DateUtil.format(new Date(), "yyyy"));
        vchrInfo.setAccountingPeriod(DateUtil.format(new Date(), "MM"));
        vchrInfo.setExplanation(entries.get(0).getExplanation());// 采用借方第一个摘要
        vchrInfo.setTotalAmount(bean.getTotalAmount());// 借方金额(方法中当做参数传递)
        vchrInfo.setPreparedDate(new Date());
        vchrInfo.setStatus(VoucherSendStatus.NOT_SEND);
        vchrInfo.setBusinessNo(bean.getTransNo());
        vchrInfo.setSourceSystem(bean.getSourceSystem());
        vchrInfo.setCaseType(bean.getCaseType());
        voucherAccountVo.setAccountVoucherInfoVo(vchrInfo);
        // 科目集合
        entries.forEach(accountVoucherEntry -> {
            accountVoucherEntry.setId(uidGenerator.getUID());
            accountVoucherEntry.setVoucherNo(vchrNo);
        });
        voucherAccountVo.setAccountVoucherEntryVos(entries);
        bean.getVoucherAccountVos().add(voucherAccountVo);
    }
    private BigDecimal vaildVoucher(List<AccountVoucherEntryVo> entries, Long voucherId) {
        //借方金额
        BigDecimal debitAmt = BigDecimal.ZERO;
        //贷方金额
        BigDecimal creditAmt = BigDecimal.ZERO;
        for (AccountVoucherEntryVo accountVoucherEntry:entries) {
            debitAmt = debitAmt.add(accountVoucherEntry.getDebitAmt());
            creditAmt= creditAmt.add(accountVoucherEntry.getCreditAmt());
        }
        if (debitAmt.compareTo(creditAmt) != 0) {
            log.error("凭证[" + voucherId + "]凭证借方:["+debitAmt+"]/贷方金额:["+creditAmt+"]不一致!");
            Assert.isTrue(debitAmt.compareTo(creditAmt) == 0, "凭证[" + voucherId + "]凭证借方["+debitAmt+"]/贷方金额:["+creditAmt+"]不一致!");
        }
       return debitAmt;
    }
}
